//
//  OperationTableViewController.m
//  OperationQueueDemo
//
//  Created by 苏沫离 on 2018/8/9.
//  Copyright © 2018年 苏沫离. All rights reserved.
//
#define CellIdentifer @"UITableViewCell"

#import "OperationTableViewController.h"
#import "OperationQueueTableViewController.h"

#import "OperationAsync.h"
#import "OperationSync.h"

@interface OperationTableViewController ()
@property (nonatomic ,strong) NSArray<NSString *> *titleArray;

@end

@implementation OperationTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(operationQueueClick)];
    
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifer];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadExitNotice:) name:NSThreadWillExitNotification object:nil];
    
    
    
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)operationQueueClick
{
    OperationQueueTableViewController *queueVC = [[OperationQueueTableViewController alloc] init];
    [self.navigationController pushViewController:queueVC animated:YES];
}

- (void)threadExitNotice:(NSNotification *)notification
{
    NSLog(@" threadExitNotice ------------ %@",notification.object);
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.titleArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer forIndexPath:indexPath];
    cell.textLabel.text = self.titleArray[indexPath.row];
    cell.textLabel.numberOfLines = 0;
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    NSLog(@"开始处理 -------- %@",NSThread.currentThread);
    if (indexPath.row == 0)
    {
//        GCDBlockTableViewController *blockVC = [[GCDBlockTableViewController alloc] init];
//        [self.navigationController pushViewController:blockVC animated:YES];
        [self customSyncOperationTaskAMethod];
    }
    else if (indexPath.row == 1)
    {
        [self customAsyncOperationTaskAMethod];
    }
    else if (indexPath.row == 2)
    {
        [self invocationOperationMethod];
    }
    else if (indexPath.row == 3)
    {
        [self blockOperationMethod];
    }
    else if (indexPath.row == 4)
    {
        [self addSyncDependencyMethod];
    }
    else if (indexPath.row == 5)
    {
        [self addAsyncDependencyMethod];
    }
    else if (indexPath.row == 6)
    {
        [self startMuchMethod];
    }
    else if (indexPath.row == 7)
    {
        [self startBlockMuchMethod];
    }
    else if (indexPath.row == 8)
    {
        [self waitUntilFinishedMethod];
    }
    else if (indexPath.row == 9)
    {
        [self operationLifeCycleMethod];
    }
    else if (indexPath.row == 10)
    {
        [self operationLifeCycleMethod1];
    }
    NSLog(@"结束处理 -------- %@",NSThread.currentThread);
}

#pragma mark - 自定义NSOperation

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    id task = (__bridge id)(context);
    NSLog(@"%@ ----- %@ === %@",task,keyPath,change[NSKeyValueChangeNewKey]);
}

#pragma mark - 自定义NSOperation

/*
[taskA addObserver:self forKeyPath:@"cancelled" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(taskA)];
[taskA addObserver:self forKeyPath:@"executing" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(taskA)];
[taskA addObserver:self forKeyPath:@"finished" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(taskA)];
[taskA addObserver:self forKeyPath:@"ready" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(taskA)];
 */


/*
我们已经使用```NSOperation```的系统定义子类```NSInvocationOperation```与```NSBlockOperation```来单独处理耗时任务，知道这两个子类默认在当前线程处理，会堵塞当前线程直到所有任务全部处理完毕，不会开辟新的线程。我们不妨自定义一个```NSOperation```的子类，来了解内部的工作机制：
 */

/*
 我们将耗时操作封装在```OperationSync```的```mian```方法中，运行程序：
 */
- (void)customSyncOperationTaskAMethod
{
    OperationSync *taskA = [[OperationSync alloc] init];
    taskA.completionBlock = ^{
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    };
    NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
    [taskA start];
}


/*
 [taskA addObserver:self forKeyPath:@"cancelled" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(taskA)];
 [taskA addObserver:self forKeyPath:@"executing" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(taskA)];
 [taskA addObserver:self forKeyPath:@"finished" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(taskA)];
 [taskA addObserver:self forKeyPath:@"ready" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(taskA)];
 */

/*
在上文，我们自定义一个```NSOperation```的子类```OperationSync```来同步处理耗时任务，了解了内部的工作机制。现在我们不妨来实现一个并发执行任务的```NSOperation```的子类：
要实现自定义并发操作类，必须添加许多额外功能:
* 重写 ```start``` 方法：将该方法更新为以异步方式执行操作，通常通过在新线程调用操作对象的```main```方法来做到这一点；
* 重写 ```main``` 方法（可选）：该方法实现与操作关联的任务，也可以直接在 ```start``` 方法实现该任务；
* 配置和管理操作的执行环境：并发操作必须设置本身的环境，并向客户端报告其状态。尤其是 ```isExecuting```、 ```isFinished``` 和 ```isAsynchronous``` 方法必须返回与操作状态有关的值，而且这 3 个方法必须具备线程安全性，当这些值改变时，还必须生成适当的键值观察通知(KVO)。

 */

/*
 我们使用并发操作```OperationAsync```，运行程序，分析打印结果：
 */

- (void)customAsyncOperationTaskAMethod
{
    OperationAsync *taskA = [[OperationAsync alloc] init];
    taskA.completionBlock = ^{
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    };
    NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
    [taskA start];
}


#pragma mark - NSInvocationOperation
/*
 [invocationOperation addObserver:self forKeyPath:@"cancelled" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(invocationOperation)];
 [invocationOperation addObserver:self forKeyPath:@"executing" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(invocationOperation)];
 [invocationOperation addObserver:self forKeyPath:@"finished" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(invocationOperation)];
 [invocationOperation addObserver:self forKeyPath:@"ready" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(invocationOperation)];
 */
- (void)invocationOperationMethod
{
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperationHandleTask:) object:@{}];
    invocationOperation.completionBlock = ^{
        NSLog(@"监听回调：taskA ------- %@",NSThread.currentThread);
    };
    [invocationOperation start];
}

- (void)invocationOperationHandleTask:(id)object
{
    NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
    [NSThread sleepForTimeInterval:3];//模拟耗时任务
    NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
}

#pragma mark - NSBlockOperation
/*
 [blockOperation addObserver:self forKeyPath:@"cancelled" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(blockOperation)];
 [blockOperation addObserver:self forKeyPath:@"executing" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(blockOperation)];
 [blockOperation addObserver:self forKeyPath:@"finished" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(blockOperation)];
 [blockOperation addObserver:self forKeyPath:@"ready" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(blockOperation)];
 */
//只要NSBlockOperation封装的操作数 > 1，就会异步执行操作
- (void)blockOperationMethod
{
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    }];
    
    [blockOperation addExecutionBlock:^{
        NSLog(@"开始执行：taskB ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:6];//模拟耗时任务
        NSLog(@"结束执行：taskB ------- %@",NSThread.currentThread);
    }];
    
    blockOperation.completionBlock = ^{
        NSLog(@"监听回调： ------- %@",NSThread.currentThread);
    };
    
    [blockOperation start];
}

#pragma mark - 依赖

- (void)addSyncDependencyMethod
{
    OperationSync *taskA = [[OperationSync alloc] init];
    taskA.completionBlock = ^{
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    };
    
    OperationAsync *taskB = [[OperationAsync alloc] init];
    taskB.completionBlock = ^{
        NSLog(@"结束执行：taskB ------- %@",NSThread.currentThread);
    };
    
    [taskA addDependency:taskB];

    NSLog(@"开始执行：taskB ======= %@",NSThread.currentThread);
    [taskB start];
    
    NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
    [taskA start];
}

- (void)addAsyncDependencyMethod
{
    OperationAsync *taskA = [[OperationAsync alloc] init];
    taskA.completionBlock = ^{
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    };
    
    OperationAsync *taskB = [[OperationAsync alloc] init];
    taskB.completionBlock = ^{
        NSLog(@"结束执行：taskB ------- %@",NSThread.currentThread);
    };
    
    [taskB addDependency:taskA];

    NSLog(@"开始执行：taskB ======= %@",NSThread.currentThread);
    [taskB start];

    NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
    [taskA start];
}

/*
默认是非并发的(non-concurrent);执行它的任务一次，就不能再次执行；
 
####1.4、```NSOperation```只能```start```一次
 
我们已经知道，```NSOperation```的从准备任务到完成任务生命周期历程；那么```start```调用一次后，再次调用还能执行任务嘛？答案是否定的！我们不妨来看一段程序：
 */
//一个operation对象就是一个单点触发的对象－－也就是说，执行它的任务一次，就不能再次执行
- (void)startMuchMethod
{
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    }];
    
    [blockOperation start];
    NSLog(@"再次执行：taskA ======= %@",NSThread.currentThread);
    [blockOperation start];
}

- (void)startBlockMuchMethod
{
    dispatch_block_t taskA = dispatch_block_create(DISPATCH_BLOCK_DETACHED, ^{
        NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    });
    
    dispatch_block_perform(DISPATCH_BLOCK_DETACHED, taskA);
    NSLog(@"再次执行：taskA ======= %@",NSThread.currentThread);
    dispatch_block_perform(DISPATCH_BLOCK_DETACHED, taskA);
}

/*
09:39:35 开始处理 -------- <NSThread: 0x60400007c8c0>{number = 1, name = main}
09:39:35 开始执行：taskA ======= <NSThread: 0x60400007c8c0>{number = 1, name = main}
09:39:37 结束执行：taskA ------- <NSThread: 0x60400007c8c0>{number = 1, name = main}
09:39:37 再次执行：taskA ======= <NSThread: 0x60400007c8c0>{number = 1, name = main}
09:39:37 开始执行：taskA ======= <NSThread: 0x60400007c8c0>{number = 1, name = main}
09:39:39 结束执行：taskA ------- <NSThread: 0x60400007c8c0>{number = 1, name = main}
09:39:39 结束处理 -------- <NSThread: 0x60400007c8c0>{number = 1, name = main}
 
 */

#pragma mark - 阻塞当前线程

/*
 
```-(void)waitUntilFinished```阻塞当前线程的执行，直到操作对象完成其任务；常用于将操作提交到队列后，调用此方法，等待该操作完成。

注意：操作对象永远不能在自身上调用此方法，并且应该避免在提交给与自身相同的操作队列的任何操作上调用它，这样做会导致操作死锁。相反，应用程序的其他部分可以根据需要调用此方法，以防止其他任务在目标操作对象完成之前完成。对于位于不同操作队列中的操作调用此方法通常是安全的，尽管如果每个操作都在另一个操作队列上等待，仍然可以创建死锁。
 */

- (void)waitUntilFinishedMethod
{
    OperationAsync *taskA = [[OperationAsync alloc] init];
    taskA.completionBlock = ^{
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    };
    NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
    [taskA start];
    [taskA waitUntilFinished];
}

#pragma mark - 生命周期

/*
我们知道```NSOperation``` 有多个操作状态，那么这些操作状态之间有什么联系呢？```NSOperation```的生命周期是什么？
我们通过下面程序来探究下：
 */

- (void)operationLifeCycleMethod
{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *blockOperationA = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    }];
    blockOperationA.name = @"com.demo.taskA";
    [blockOperationA addObserver:self forKeyPath:@"ready" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(blockOperationA)];
    [blockOperationA addObserver:self forKeyPath:@"cancelled" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(blockOperationA)];
    [blockOperationA addObserver:self forKeyPath:@"executing" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(blockOperationA)];
    [blockOperationA addObserver:self forKeyPath:@"finished" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(blockOperationA)];
    
    NSBlockOperation *blockOperationB = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"开始执行：taskB ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
        NSLog(@"结束执行：taskB ------- %@",NSThread.currentThread);
    }];
    blockOperationB.name = @"com.demo.taskB";
    [blockOperationB addObserver:self forKeyPath:@"ready" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(blockOperationB)];
    [blockOperationB addObserver:self forKeyPath:@"cancelled" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(blockOperationB)];
    [blockOperationB addObserver:self forKeyPath:@"executing" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(blockOperationB)];
    [blockOperationB addObserver:self forKeyPath:@"finished" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:(__bridge void * _Nullable)(blockOperationB)];
    
    NSLog(@"blockOperationA.ready --- %d",blockOperationA.ready);
    NSLog(@"blockOperationB.ready --- %d",blockOperationB.ready);
    [blockOperationA addDependency:blockOperationB];
    NSLog(@"blockOperationA.ready === %d",blockOperationA.ready);
    NSLog(@"blockOperationB.ready === %d",blockOperationB.ready);
    
    [queue addOperation:blockOperationA];
    [queue addOperation:blockOperationB];
}

/*
 
运行程序，分析打印数据：

```
11:42:09 开始处理 -------- <NSThread: 0x604000076300>{number = 1, name = main}
11:42:09 blockOperationA.ready --- 1
11:42:09 blockOperationB.ready --- 1
11:42:09 <NSBlockOperation: 0x6000004401e0>{name = 'com.demo.taskA'} ----- ready === 0
11:42:09 blockOperationA.ready === 0
11:42:09 blockOperationB.ready === 1
11:42:09 结束处理 -------- <NSThread: 0x604000076300>{number = 1, name = main}
11:42:09 <NSBlockOperation: 0x600000441800>{name = 'com.demo.taskB'} ----- executing === 1
11:42:09 开始执行：taskB ======= <NSThread: 0x60000047cac0>{number = 3, name = (null)}
11:42:11 结束执行：taskB ------- <NSThread: 0x60000047cac0>{number = 3, name = (null)}
11:42:11 <NSBlockOperation: 0x6000004401e0>{name = 'com.demo.taskA'} ----- executing === 1
11:42:11 <NSBlockOperation: 0x6000004401e0>{name = 'com.demo.taskA'} ----- ready === 1
11:42:11 开始执行：taskA ======= <NSThread: 0x60400046d880>{number = 4, name = (null)}
11:42:11 <NSBlockOperation: 0x600000441800>{name = 'com.demo.taskB'} ----- executing === 0
11:42:11 <NSBlockOperation: 0x600000441800>{name = 'com.demo.taskB'} ----- finished === 1
11:42:13 结束执行：taskA ------- <NSThread: 0x60400046d880>{number = 4, name = (null)}
11:42:13 <NSBlockOperation: 0x6000004401e0>{name = 'com.demo.taskA'} ----- executing === 0
11:42:13 <NSBlockOperation: 0x6000004401e0>{name = 'com.demo.taskA'} ----- finished === 1
```
 
分析打印数据：
* 通过```blockOperation.ready```可以看到，```NSOperation```的属性```ready```值默认为YES，处于准备就绪状态；当给该```NSOperation```添加一个依赖后，属性```ready```变为 NO，处于未准备状态。
* ```taskB```结束执行后该操作属性```finished```变为 YES；这时```taskA```操作的属性```ready```值改为YES，表示```taskA```的操作已经准备就绪，可以开始执行；
* 操作```taskA```的属性```executing```变为 YES，表示正在执行；```taskA```执行完毕之后，```executing```变为 NO，```finished```变为 1。
 
我们通过KVO监听操作```taskA```的各属性变化，可以得出```NSOperation```的生命周期：

 */


- (void)operationLifeCycleMethod1
{
    NSBlockOperation *blockOperationA = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
    }];
    NSBlockOperation *blockOperationB = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"开始执行：taskB ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:2];//模拟耗时任务
    }];
    [blockOperationA addDependency:blockOperationB];//添加依赖
    NSLog(@"blockOperationA.ready === %d",blockOperationA.ready);
    NSLog(@"blockOperationB.ready === %d",blockOperationB.ready);
    
    [blockOperationA start];
    [blockOperationB start];
}


/*
 运行这段代码，发现程序异常终止：接收器还没有准备好执行
 Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSOperationInternal _start:]: receiver is not yet ready to execute'

 
 */

#pragma mark - setter and getter

- (NSArray<NSString *> *)titleArray
{
    if (_titleArray == nil)
    {
        _titleArray = @[@"同步NSOperation",
                        @"异步NSOperation",
                        @"NSInvocationOperation",
                        @"NSBlockOperation",
                        @"同步NSOperation依赖",
                        @"异步NSOperation依赖",
                        @"多次 start",
                        @"多次 dispatch_block",
                        @"waitUntilFinished",
                        @"life cycle",
                        @"life cycle1"];
    }
    return _titleArray;
}


@end
